const { expect } = require("chai");
const { ethers } = require("hardhat");
const { parseEther } = require("ethers");

describe("Crowdfunding", function () {
  let Crowdfunding, crowdfunding, owner, addr1, addr2;

  beforeEach(async function () {
  Crowdfunding = await ethers.getContractFactory("Crowdfunding");
  [owner, addr1, addr2] = await ethers.getSigners();
  crowdfunding = await Crowdfunding.deploy();
  await crowdfunding.waitForDeployment();
  });

  it("should create a project", async function () {
  await expect(crowdfunding.createProject("Test Project", parseEther("1"), 1000))
      .to.emit(crowdfunding, "ProjectCreated");
    const project = await crowdfunding.projects(1);
    expect(project.creator).to.equal(owner.address);
  expect(project.goal).to.equal(parseEther("1"));
    expect(project.amountRaised).to.equal(0);
  });

  it("should allow funding a project", async function () {
  await crowdfunding.createProject("Test Project", parseEther("1"), 1000);
  await expect(crowdfunding.connect(addr1).fundProject(1, { value: parseEther("0.5") }))
      .to.emit(crowdfunding, "Funded");
    const project = await crowdfunding.projects(1);
  expect(project.amountRaised).to.equal(parseEther("0.5"));
  const contribution = await crowdfunding.getContribution(1, addr1.address);
  expect(contribution).to.equal(parseEther("0.5"));
  });

  it("should not allow creator to fund own project", async function () {
    await crowdfunding.createProject("Test Project", parseEther("1"), 1000);
    await expect(crowdfunding.fundProject(1, { value: parseEther("0.1") }))
      .to.be.revertedWith("Creator cannot fund own project");
  });

  it("should not allow funding after deadline", async function () {
    await crowdfunding.createProject("Test Project", parseEther("1"), 1);
    await ethers.provider.send("evm_increaseTime", [2]);
    await ethers.provider.send("evm_mine", []);
    await expect(crowdfunding.connect(addr1).fundProject(1, { value: parseEther("0.1") }))
      .to.be.revertedWith("Project deadline passed");
  });

  it("should allow creator to withdraw after goal met and deadline", async function () {
  await crowdfunding.createProject("Test Project", parseEther("1"), 2);
  await crowdfunding.connect(addr1).fundProject(1, { value: parseEther("1") });
  await ethers.provider.send("evm_increaseTime", [3]);
  await ethers.provider.send("evm_mine", []);
  await expect(crowdfunding.withdraw(1)).to.emit(crowdfunding, "Withdrawn");
  const project = await crowdfunding.projects(1);
  expect(project.withdrawn).to.equal(true);
  });

  it("should allow withdraw before deadline if goal reached", async function () {
  await crowdfunding.createProject("Test Project", parseEther("1"), 1000);
  await crowdfunding.connect(addr1).fundProject(1, { value: parseEther("1") });
  // since funding reached goal, creator should be able to withdraw even before deadline
  await expect(crowdfunding.withdraw(1)).to.emit(crowdfunding, "Withdrawn");
  const project = await crowdfunding.projects(1);
  expect(project.withdrawn).to.equal(true);
  });

  it("should not allow withdraw if goal not met", async function () {
  await crowdfunding.createProject("Test Project", parseEther("1"), 2);
  await crowdfunding.connect(addr1).fundProject(1, { value: parseEther("0.5") });
  await ethers.provider.send("evm_increaseTime", [3]);
  await ethers.provider.send("evm_mine", []);
  await expect(crowdfunding.withdraw(1)).to.be.revertedWith("Goal not reached");
  });

  it("should not allow double withdraw", async function () {
  await crowdfunding.createProject("Test Project", parseEther("1"), 2);
  await crowdfunding.connect(addr1).fundProject(1, { value: parseEther("1") });
  await ethers.provider.send("evm_increaseTime", [3]);
  await ethers.provider.send("evm_mine", []);
  await crowdfunding.withdraw(1);
  await expect(crowdfunding.withdraw(1)).to.be.revertedWith("Already withdrawn");
  });
});
